use super::*;
use crate::Result;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use chrono::{DateTime, Utc};

/// Configuration backup manager
pub struct ConfigBackupManager {
    backup_dir: PathBuf,
    max_backups: usize,
}

impl ConfigBackupManager {
    /// Create a new backup manager
    pub fn new(backup_dir: PathBuf, max_backups: usize) -> Result<Self> {
        utils::ensure_dir_exists(&backup_dir)?;
        
        Ok(Self {
            backup_dir,
            max_backups,
        })
    }
    
    /// Create with default backup directory
    pub fn with_default_dir(max_backups: usize) -> Result<Self> {
        let backup_dir = ConfigPaths::data_dir()?.join("backups");
        Self::new(backup_dir, max_backups)
    }
    
    /// Create a backup of the given configuration
    pub fn create_backup(&self, config: &AppConfig, backup_type: BackupType) -> Result<BackupInfo> {
        let timestamp = Utc::now();
        let backup_filename = self.generate_backup_filename(&timestamp, &backup_type);
        let backup_path = self.backup_dir.join(&backup_filename);
        
        // Serialize configuration to JSON
        let config_json = serde_json::to_string_pretty(config)?;
        
        // Create backup metadata
        let metadata = BackupMetadata {
            created_at: timestamp,
            backup_type,
            config_version: config.version.clone(),
            file_size: config_json.len() as u64,
            checksum: self.calculate_checksum(&config_json),
        };
        
        // Create backup file with metadata
        let backup_data = BackupData {
            metadata: metadata.clone(),
            config: config.clone(),
        };
        
        let backup_json = serde_json::to_string_pretty(&backup_data)?;
        fs::write(&backup_path, backup_json)?;
        
        // Clean up old backups if necessary
        self.cleanup_old_backups()?;
        
        Ok(BackupInfo {
            path: backup_path,
            metadata,
        })
    }
    
    /// Create an automatic backup
    pub fn create_auto_backup(&self, config: &AppConfig) -> Result<BackupInfo> {
        self.create_backup(config, BackupType::Automatic)
    }
    
    /// Create a manual backup
    pub fn create_manual_backup(&self, config: &AppConfig, description: Option<String>) -> Result<BackupInfo> {
        self.create_backup(config, BackupType::Manual { description })
    }
    
    /// Create a pre-migration backup
    pub fn create_migration_backup(&self, config: &AppConfig, from_version: String, to_version: String) -> Result<BackupInfo> {
        self.create_backup(config, BackupType::PreMigration { from_version, to_version })
    }
    
    /// List all available backups
    pub fn list_backups(&self) -> Result<Vec<BackupInfo>> {
        if !self.backup_dir.exists() {
            return Ok(Vec::new());
        }
        
        let mut backups = Vec::new();
        let entries = fs::read_dir(&self.backup_dir)?;
        
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            
            if path.is_file() && path.extension().map_or(false, |ext| ext == "json") {
                if let Ok(backup_info) = self.load_backup_info(&path) {
                    backups.push(backup_info);
                }
            }
        }
        
        // Sort by creation time (newest first)
        backups.sort_by(|a, b| b.metadata.created_at.cmp(&a.metadata.created_at));
        
        Ok(backups)
    }
    
    /// Restore configuration from a backup
    pub fn restore_backup(&self, backup_path: &PathBuf) -> Result<AppConfig> {
        if !backup_path.exists() {
            return Err(ConfigError::FileNotFound(backup_path.display().to_string()).into());
        }
        
        let backup_content = fs::read_to_string(backup_path)?;
        let backup_data: BackupData = serde_json::from_str(&backup_content)?;
        
        // Verify backup integrity
        self.verify_backup_integrity(&backup_data)?;
        
        Ok(backup_data.config)
    }
    
    /// Delete a specific backup
    pub fn delete_backup(&self, backup_path: &PathBuf) -> Result<()> {
        if backup_path.exists() {
            fs::remove_file(backup_path)?;
        }
        Ok(())
    }
    
    /// Clean up old backups, keeping only the specified number
    pub fn cleanup_old_backups(&self) -> Result<()> {
        let backups = self.list_backups()?;
        
        if backups.len() <= self.max_backups {
            return Ok(());
        }
        
        // Keep the newest backups, delete the rest
        for backup in backups.iter().skip(self.max_backups) {
            if let Err(e) = self.delete_backup(&backup.path) {
                eprintln!("Warning: Failed to delete backup {}: {}", 
                         backup.path.display(), e);
            }
        }
        
        Ok(())
    }
    
    /// Get backup statistics
    pub fn get_backup_stats(&self) -> Result<BackupStats> {
        let backups = self.list_backups()?;
        
        let total_count = backups.len();
        let total_size = backups.iter()
            .map(|b| b.metadata.file_size)
            .sum();
        
        let mut type_counts = std::collections::HashMap::new();
        for backup in &backups {
            let type_name = match &backup.metadata.backup_type {
                BackupType::Automatic => "automatic",
                BackupType::Manual { .. } => "manual",
                BackupType::PreMigration { .. } => "pre_migration",
            };
            *type_counts.entry(type_name.to_string()).or_insert(0) += 1;
        }
        
        let oldest_backup = backups.last().map(|b| b.metadata.created_at);
        let newest_backup = backups.first().map(|b| b.metadata.created_at);
        
        Ok(BackupStats {
            total_count,
            total_size,
            type_counts,
            oldest_backup,
            newest_backup,
        })
    }
    
    /// Export backup to a specific location
    pub fn export_backup(&self, backup_path: &PathBuf, export_path: &PathBuf) -> Result<()> {
        if !backup_path.exists() {
            return Err(ConfigError::FileNotFound(backup_path.display().to_string()).into());
        }
        
        fs::copy(backup_path, export_path)?;
        Ok(())
    }
    
    /// Import backup from an external file
    pub fn import_backup(&self, import_path: &PathBuf) -> Result<BackupInfo> {
        if !import_path.exists() {
            return Err(ConfigError::FileNotFound(import_path.display().to_string()).into());
        }
        
        // Load and validate the backup
        let backup_content = fs::read_to_string(import_path)?;
        let backup_data: BackupData = serde_json::from_str(&backup_content)?;
        
        // Verify backup integrity
        self.verify_backup_integrity(&backup_data)?;
        
        // Generate new filename for imported backup
        let timestamp = Utc::now();
        let backup_filename = format!("imported_{}.json", 
                                    timestamp.format("%Y%m%d_%H%M%S"));
        let backup_path = self.backup_dir.join(&backup_filename);
        
        // Copy to backup directory
        fs::copy(import_path, &backup_path)?;
        
        Ok(BackupInfo {
            path: backup_path,
            metadata: backup_data.metadata,
        })
    }
    
    // Private helper methods
    
    /// Generate backup filename
    fn generate_backup_filename(&self, timestamp: &DateTime<Utc>, backup_type: &BackupType) -> String {
        let type_prefix = match backup_type {
            BackupType::Automatic => "auto",
            BackupType::Manual { .. } => "manual",
            BackupType::PreMigration { .. } => "migration",
        };
        
        format!("{}_{}.json", type_prefix, timestamp.format("%Y%m%d_%H%M%S"))
    }
    
    /// Load backup information from file
    fn load_backup_info(&self, path: &PathBuf) -> Result<BackupInfo> {
        let backup_content = fs::read_to_string(path)?;
        let backup_data: BackupData = serde_json::from_str(&backup_content)?;
        
        Ok(BackupInfo {
            path: path.clone(),
            metadata: backup_data.metadata,
        })
    }
    
    /// Calculate checksum for backup verification
    fn calculate_checksum(&self, data: &str) -> String {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};
        
        let mut hasher = DefaultHasher::new();
        data.hash(&mut hasher);
        format!("{:x}", hasher.finish())
    }
    
    /// Verify backup integrity
    fn verify_backup_integrity(&self, backup_data: &BackupData) -> Result<()> {
        let config_json = serde_json::to_string(&backup_data.config)?;
        let calculated_checksum = self.calculate_checksum(&config_json);
        
        if calculated_checksum != backup_data.metadata.checksum {
            return Err(ConfigError::BackupFailed("Backup integrity check failed".to_string()).into());
        }
        
        Ok(())
    }
}

/// Backup type enumeration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BackupType {
    /// Automatic backup created by the system
    Automatic,
    /// Manual backup created by the user
    Manual { description: Option<String> },
    /// Backup created before configuration migration
    PreMigration { from_version: String, to_version: String },
}

/// Backup metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupMetadata {
    pub created_at: DateTime<Utc>,
    pub backup_type: BackupType,
    pub config_version: String,
    pub file_size: u64,
    pub checksum: String,
}

/// Complete backup data structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupData {
    pub metadata: BackupMetadata,
    pub config: AppConfig,
}

/// Backup information
#[derive(Debug, Clone)]
pub struct BackupInfo {
    pub path: PathBuf,
    pub metadata: BackupMetadata,
}

/// Backup statistics
#[derive(Debug, Clone)]
pub struct BackupStats {
    pub total_count: usize,
    pub total_size: u64,
    pub type_counts: std::collections::HashMap<String, usize>,
    pub oldest_backup: Option<DateTime<Utc>>,
    pub newest_backup: Option<DateTime<Utc>>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    
    #[test]
    fn test_backup_manager() {
        let temp_dir = TempDir::new().unwrap();
        let backup_manager = ConfigBackupManager::new(temp_dir.path().to_path_buf(), 5).unwrap();
        
        let config = AppConfig::default();
        
        // Test creating backup
        let backup_info = backup_manager.create_auto_backup(&config).unwrap();
        assert!(backup_info.path.exists());
        
        // Test listing backups
        let backups = backup_manager.list_backups().unwrap();
        assert_eq!(backups.len(), 1);
        
        // Test restoring backup
        let restored_config = backup_manager.restore_backup(&backup_info.path).unwrap();
        assert_eq!(restored_config.version, config.version);
        
        // Test backup stats
        let stats = backup_manager.get_backup_stats().unwrap();
        assert_eq!(stats.total_count, 1);
        assert!(stats.total_size > 0);
    }
    
    #[test]
    fn test_backup_cleanup() {
        let temp_dir = TempDir::new().unwrap();
        let backup_manager = ConfigBackupManager::new(temp_dir.path().to_path_buf(), 2).unwrap();
        
        let config = AppConfig::default();
        
        // Create multiple backups
        for _ in 0..5 {
            backup_manager.create_auto_backup(&config).unwrap();
            std::thread::sleep(std::time::Duration::from_millis(10)); // Ensure different timestamps
        }
        
        let backups = backup_manager.list_backups().unwrap();
        assert_eq!(backups.len(), 2); // Should keep only 2 backups
    }
}

/// Automatic backup scheduler
pub struct AutoBackupScheduler {
    backup_manager: ConfigBackupManager,
    config_manager: Arc<ConfigManager>,
    interval: std::time::Duration,
    is_running: Arc<std::sync::atomic::AtomicBool>,
}

impl AutoBackupScheduler {
    /// Create a new auto backup scheduler
    pub fn new(
        backup_manager: ConfigBackupManager,
        config_manager: Arc<ConfigManager>,
        interval_minutes: u64,
    ) -> Self {
        Self {
            backup_manager,
            config_manager,
            interval: std::time::Duration::from_secs(interval_minutes * 60),
            is_running: Arc::new(std::sync::atomic::AtomicBool::new(false)),
        }
    }

    /// Start the automatic backup scheduler
    pub async fn start(&self) -> Result<()> {
        if self.is_running.load(std::sync::atomic::Ordering::Relaxed) {
            return Ok(()); // Already running
        }

        self.is_running.store(true, std::sync::atomic::Ordering::Relaxed);

        let backup_manager = self.backup_manager.clone();
        let config_manager = self.config_manager.clone();
        let interval = self.interval;
        let is_running = self.is_running.clone();

        tokio::spawn(async move {
            let mut interval_timer = tokio::time::interval(interval);

            while is_running.load(std::sync::atomic::Ordering::Relaxed) {
                interval_timer.tick().await;

                // Create automatic backup
                let config = config_manager.get_config().await;
                if let Err(e) = backup_manager.create_auto_backup(&config) {
                    eprintln!("Failed to create automatic backup: {}", e);
                }
            }
        });

        Ok(())
    }

    /// Stop the automatic backup scheduler
    pub fn stop(&self) {
        self.is_running.store(false, std::sync::atomic::Ordering::Relaxed);
    }

    /// Check if the scheduler is running
    pub fn is_running(&self) -> bool {
        self.is_running.load(std::sync::atomic::Ordering::Relaxed)
    }
}

impl Clone for ConfigBackupManager {
    fn clone(&self) -> Self {
        Self {
            backup_dir: self.backup_dir.clone(),
            max_backups: self.max_backups,
        }
    }
}
